import { computed, defineComponent, ref, getCurrentInstance } from 'vue'
import { isArray } from '@vue/shared'
import { castArray } from '../_util/_'
import { getComponent } from '../_util/props-util'
import { useMemo, useNameSpace } from '../_util/hooks'
import isValid from '../_util/isValid'

import Icon from '../icon'
import inputProps from './inputProps'
import { calcTextareaHeight } from './utils'

const prefixCls = 'x-input'

export default defineComponent({
  name: prefixCls,
  props: inputProps,
  emits: ['update:value', 'change', 'input', 'pressEnter', 'keydown', 'focus', 'blur'],
  setup(props, { emit, expose }) {
    const ns = useNameSpace(prefixCls)
    const vm = getCurrentInstance().proxy
    const inputRef = ref<HTMLInputElement>()
    const focusedRef = ref<boolean>()
    const valRef = useMemo(() => (props.pair ? castArray(props.value) : props.value))
    const showClearRef = computed(() => castArray(valRef.value).some(isValid) && !props.disabled)

    // ========================== Event ==========================
    function onInput(e?: Event) {
      const value = (e?.target as HTMLInputElement)?.value ?? ''
      if (props.pair) {
        if (!isArray(valRef.value)) {
          throw new Error('valRef.value is not a Array type')
        }
        if (e) {
          if (e.target == inputRef.value) valRef.value[0] = value
          else valRef.value[1] = value
        } else {
          valRef.value = ['', '']
        }
      } else {
        valRef.value = value
      }
      emit('update:value', valRef.value)
      emit('input', valRef.value)
    }
    function onChange(e: Event) {
      emit('change', valRef.value)
    }
    function onFocus(e: FocusEvent) {
      focusedRef.value = true
      emit('focus', e)
    }
    function onBlur(e: FocusEvent) {
      focusedRef.value = false
      emit('blur', e)
    }
    function onKeydown(e: KeyboardEvent) {
      if (e.keyCode === 13) {
        emit('pressEnter', e)
      }
      emit('keydown', e)
    }
    function onClearDown() {
      window.requestAnimationFrame(focus)
    }
    function onClear(e: MouseEvent) {
      e.stopPropagation()
      inputRef.value.value = ''
      onInput()
      focus()
    }

    // ========================== Expose ==========================
    function focus() {
      inputRef.value.focus()
    }
    function blur() {
      inputRef.value.blur()
    }

    expose({
      focus,
      blur,
      get value() {
        return valRef.value
      }
    })

    // ========================== Render ==========================
    const on = { onInput, onChange, onKeydown, onFocus, onBlur }

    function renderInput() {
      const { disabled, type } = props
      const placeholder = castArray(props.placeholder)
      const value = castArray(valRef.value)

      const addon1Node = getComponent(vm, 'addonBefore')
      const addon2Node = getComponent(vm, 'addonAfter')
      const prefixNode = getComponent(vm, 'prefix')
      const suffixNode = getComponent(vm, 'suffix')

      return (
        <>
          {addon1Node && <span class={ns.e('addon')}>{addon1Node}</span>}
          {prefixNode && <span class={ns.e('prefix')}>{prefixNode}</span>}
          {<input class={ns.e('input')} ref={inputRef} value={value[0]} type={type} disabled={disabled} placeholder={placeholder[0]} {...on} />}
          {props.pair && <span class={ns.e('separator')}>{props.separator}</span>}
          {props.pair && <input class={ns.e('input')} value={value[1]} type={type} disabled={disabled} placeholder={placeholder[1] || placeholder[0]} {...on} />}
          {suffixNode && <span class={ns.e('suffix')}>{suffixNode}</span>}
          {addon2Node && <span class={ns.e('addon')}>{addon2Node}</span>}
        </>
      )
    }

    function renderTextarea() {
      const { disabled, placeholder } = props
      const style = calcTextareaHeight(inputRef.value, props.autosize)
      return <textarea class={ns.e('input')} ref={inputRef} style={style} value={valRef.value} disabled={disabled} placeholder={placeholder} {...on} />
    }

    return () => {
      const { disabled, clearable } = props

      const cls = [ns.b(), ns.m(props.type), props.pair && ns.m('pair'), disabled && ns.m('disabled'), focusedRef.value && ns.m(`focus`)]

      return (
        <div class={cls}>
          {props.type !== 'textarea' ? renderInput() : renderTextarea()}
          {clearable && (
            <span v-show={showClearRef.value} class={`${prefixCls}_clear`} onMousedown={onClearDown} onClick={onClear}>
              <Icon type='cross' />
            </span>
          )}
        </div>
      )
    }
  }
})
